package com.smartao.base.acl;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.security.taglibs.TagLibConfig;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.smartao.base.employee.Employee;
import com.smartao.core.UserContext;
import com.smartao.core.orm.entity.BaseEntity;

public class AccessControlTag<T extends BaseEntity<T>> extends TagSupport{
	private static final long serialVersionUID = 1L;
	protected static final Log logger = LogFactory.getLog(AccessControlTag.class);
	
	private ApplicationContext applicationContext;
	private Object domainObject;
    private String hasPermission = "";
    private String var;

    protected void init(){
    	if (applicationContext != null) {
            return;
        }
        this.applicationContext = getContext(pageContext);
    }
    
    protected ApplicationContext getContext(PageContext pageContext) {
        ServletContext servletContext = pageContext.getServletContext();
        return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
    }
    
    private int skipBody() {
        if (var != null) {
            pageContext.setAttribute(var, Boolean.FALSE, PageContext.PAGE_SCOPE);
        }
        return TagLibConfig.evalOrSkip(false);
    }

    private int evalBody() {
        if (var != null) {
            pageContext.setAttribute(var, Boolean.TRUE, PageContext.PAGE_SCOPE);
        }
        return TagLibConfig.evalOrSkip(true);
    }
    
    private List<Permission> parseHasPermission(String hasPermission){
		String[] requiredPermissions = hasPermission.split(",");
		List<Permission> list = new ArrayList<Permission>();
		for (String requiredPermission : requiredPermissions) {
			Permission permission = PermissionFactory.getByName(requiredPermission);
			if (permission != null) {
				list.add(permission);
			}
		}
		return list;
    }
    
	@Override
	@SuppressWarnings("unchecked")
	public int doStartTag() throws JspException {
		Employee user = UserContext.getCurrentUser();
		if (user==null){
			if (logger.isDebugEnabled()) {
                logger.debug(
                    "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
            }
            return skipBody();
		}
		
		if ((null == hasPermission) || "".equals(hasPermission)) {
            return skipBody();
        }

		init();

        if (domainObject == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("domainObject resolved to null, so including tag body");
            }
            return evalBody();
        }

        List<Permission> requiredPermissions = parseHasPermission(hasPermission);
        
        PermissionService permissionService = (PermissionService) applicationContext.getBean(PermissionService.class);
        for(Permission requiredPermission : requiredPermissions) {
            if (!permissionService.isGranted(user, (T)domainObject, requiredPermission)) {
                return skipBody();
            }
        }

        return evalBody();
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	public Object getDomainObject() {
		return domainObject;
	}

	public void setDomainObject(Object domainObject) {
		this.domainObject = domainObject;
	}

	public String getHasPermission() {
		return hasPermission;
	}

	public void setHasPermission(String hasPermission) {
		this.hasPermission = hasPermission;
	}

	public String getVar() {
		return var;
	}

	public void setVar(String var) {
		this.var = var;
	}
	
}
